查看原文
其他

Web Worker的正确打开方式

大转转FE 大转转FE 2023-02-16

定义

Web Worker在MDN上的解释为通过使用Web Workers,Web应用程序可以在独立于主线程的后台线程中,运行一个脚本操作。这样做的好处是可以在独立线程中执行费时的处理任务,从而允许主线程(通常是UI线程)不会因此被阻塞/放慢。

使用场景

Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅。基于这一特性或者说优点,Web Worker衍生出了以下几种使用场景:

1.加密:端到端的加密由于对保护个人和敏感数据日益严格的法律规定而变得越来越流行。加密有时候会非常地耗时,特别是如果当你需要经常加密很多数据的时候(比如,发往服务器前加密数据)。这是一个使用 Web Worker 的绝佳场景,因为它并不需要访问 DOM 或者利用其它魔法-它只是纯粹使用算法进行计算而已。一旦在 worker 进行计算,它对于用户来说是无缝地且不会影响到用户体验。

2.预取数据:为了优化网站或者网络应用及提升数据加载时间,你可以使用 Workers 来提前加载部分数据以备不时之需。不像其它技术,Web Workers 在这种情况下是最棒哒,因为它不会影响程序的使用体验。

3.拼写检查:一个基本的拼写检测器是这样工作的-程序会读取一个包含拼写正确的单词列表的字典文件。字典会被解析成一个搜索树以加快实际的文本搜索。当检查器检查一个单词的时候,程序会在预构建搜索树中进行检索。如果在树中没有检索到,则会通过提供替代的字符为用户提供替代的拼写并检测单词是否是有效-是否是用户需要的单词。这个检索过程中的所有工作都可以交由 Web Worker 来完成,这样用户就只需输入单词和语句而不会阻塞 UI,与此同时 worker 会处理所有的搜索和服务建议。

分类

Dedicated Workers(专用线程):由主进程实例化并且只能与之进行通信。

Shared workers(共享线程):可以被运行在同源的所有进程访问(不同的浏览的选项卡,内联框架及其它shared workers)。

Service Workers(服务线程):由事件驱动的 worker,它由源和路径组成。它可以控制它关联的网页,解释且修改导航,资源的请求,以及一种非常细粒度的方式来缓存资源以让你非常灵活地控制程序在某些情况下的行为(比如网络不可用)。

使用方法

  • 新建一个Worker线程
const worker = new Worker('worker.js')
  • 主线程向Worker发消息
worker.postMessage('post message');
  • 主线程接收子线程发回来的消息
worker.onmessage = function (event) {
  doSomething();
}

function doSomething() {
  // 执行任务
  worker.postMessage('Work done!');
}
  • 主线程中终止worker
worker.terminate()
  • 出错处理
worker.onerror = function(e){ 
//打印出错消息 
console.log(e.message);
//中断与子线程的联系 
worker.terminate();
}

如今前端多为SPA项目,如使用Web Worker,还需要做一些处理。以下为在ES6+Webpack 的代码工程环境下的使用方法。

  • 安装依赖
$ npm install -D worker-loader # 或 $ yarn add worker-loader --dev
  • 代码中直接使用 worker-loader
// main.js
const MyWorker = require("worker-loader!./file.js");
const worker = new MyWorker();
worker.postMessage({a: 1});
worker.onmessage = function(event) { /* 操作 */ };
worker.addEventListener("message"function(event) { /* 操作 */ });

优点:写 worker 逻辑的脚本文件可以任意命名,只要传进 worker-loader 中处理即可;缺点:每引入一次 worker 逻辑的脚本文件,就需要写一次如上所示的代码,需要多写 N(N>=1) 次的 "worker-loader!"

  • 在 webpack 的配置文件中引入 worker-loader
rules: [
  {
    // 匹配 *.worker.js
    test: /\.worker\.js$/,
    use: {
      loader: 'worker-loader',
      options: {
        name: '[name]:[hash:8].js',
      }
    }
  }
]

局限性

1.同源限制:分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。

2.DOM 限制:Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用documentwindowparent这些对象。但是,Worker 线程可以navigator对象和location对象。

3.通信联系:Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。

4.脚本限制:Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。

5.文件限制:Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。

兼容性

如下如,专用线程Worker的兼容性是较好的,但SharedWorker与ServiceWorker的兼容性不太理想。

总结

虽然可以通过使用Web Worker将代码挪到另一个不同的线程执行从而任由js代码阻塞,但是我们在实际工作中由于上文提到的局限性以及兼容性导致用到Web Worker的场景是屈指可数的。不过通过众多fe的努力以及不断发展的大前端,我相信Web Worker肯定会有发光发热的一天。

参考文章

  • https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API
  • http://www.ruanyifeng.com/blog/2018/07/web-worker.html
  • https://segmentfault.com/a/1190000014938305


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存